package com.ruoyi.bluebera.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.bluebera.conf.ChainConfigLoader;
import com.ruoyi.bluebera.domain.*;
import com.ruoyi.bluebera.service.IBizTxRecordService;
import com.ruoyi.bluebera.service.IBizUsersService;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.*;
import java.net.Proxy;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * 定时任务：从 etherscan.io v2 api 获取收币地址的交易
 */
@Component("allChainTxFetcher")
public class AllChainTxFetcher {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS) // 连接超时
            .readTimeout(15, TimeUnit.SECONDS)    // 读超时
            .writeTimeout(10, TimeUnit.SECONDS)   // 写超时
            //.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080))) // 本地 HTTP 代理
            .build();

    @Autowired
    private ChainConfigLoader chainConfigLoader;
    @Autowired
    private IBizTxRecordService bizTxRecordService;
    @Autowired
    private IBizUsersService bizUsersService;


    /**
     * 定时任务入口方法
     * 在后台配置 cron 表达式触发
     */
    public void fetchTransactions() {
        // 示例：收币地址列表（可改为从数据库查询）
        BizUsers bizUsers = new BizUsers();
        bizUsers.setValid(0);
        List<BizUsers> listUser = bizUsersService.selectBizUsersList(bizUsers);


        // 获取链配置
        JSONArray chains = chainConfigLoader.getChainsFromRedis();
        if (chains == null || chains.isEmpty()) {
            System.err.println("chains 配置未加载到 Redis");
            logger.info("chains 配置未加载到 Redis");
            return;
        }
        List<BizTxRecord> bizTxRecordList = null; // for fetchxxx
        // 遍历收币地址
        for (BizUsers each : listUser) {
            // 遍历链
            for (int i = 0; i < chains.size(); i++) {
                JSONObject chain = chains.getJSONObject(i);
                String chainName = chain.getString("name");
                int chainId = chain.getIntValue("chainId");
                JSONArray tokens = chain.getJSONArray("tokens");

                // 遍历代币
                for (int j = 0; j < tokens.size(); j++) {
                    JSONObject token = tokens.getJSONObject(j);
                    String symbol = token.getString("symbol");
                    String contract = token.getString("address"); // native 币可能为 null

                    // 调用 etherscan v2 API 获取收币地址对应的交易记录
                    try {
                        if (contract != null && !"null".equals(contract)) {
                            // 代币交易
                            bizTxRecordList = fetchTokenTxRecords(
                                    ""+chainId,
                                    each.getWalletAddress(),
                                    contract,
                                    TaskConstants.BERA_OFFSET
                            );
                        } else {
                            // 原生币交易
                            bizTxRecordList = fetchNativeTxRecords(
                                    ""+chainId,
                                    each.getWalletAddress(),
                                    TaskConstants.BERA_OFFSET) ;
                        }
                        // 存储收币地址交易到 biz_tx_record
                        for (BizTxRecord record : bizTxRecordList) {
                            BizTxRecord tx = new BizTxRecord();
                            tx.setHash(record.getHash());
                            if(bizTxRecordService.selectBizTxRecordList(tx).size()==0) {
                                bizTxRecordService.insertBizTxRecord(record); // todo -- address check
                            }
                        }

                    } catch (Exception e) {
                        System.err.println("请求链 " + chainName + " 失败: " + e.getMessage());
                    }

                    // 休息 1 秒，避免 API 调用过于频繁
                    try {
                        TimeUnit.SECONDS.sleep(TaskConstants.SLEEP_SECONDS);   // 更可读
                        // 或 Thread.sleep(1000);    // 传统写法
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt(); // 恢复中断状态
                        e.printStackTrace();
                    }


                }
            }
        }
    }


    public List<BizTxRecord> fetchNativeTxRecords(String chainId, String address, int offset) throws IOException {
        List<BizTxRecord> records = new ArrayList<>();

        HttpUrl url = HttpUrl.parse(TaskConstants.BASE_URL).newBuilder()
                .addQueryParameter("chainid", chainId)
                .addQueryParameter("module", "account")
                .addQueryParameter("action", "txlist")
                .addQueryParameter("address", address)
                .addQueryParameter("startblock", "0")
                .addQueryParameter("endblock", "99999999")
                .addQueryParameter("page", "1")
                .addQueryParameter("offset", String.valueOf(offset))
                .addQueryParameter("sort", "desc")
                .addQueryParameter("apikey", TaskConstants.API_KEY)
                .build();
        logger.info("wdy url={}", url);
        Request request = new Request.Builder().url(url).get().build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected HTTP code: " + response.code());
            }

            String body = response.body().string();
            com.alibaba.fastjson.JSONObject json = JSON.parseObject(body);

            if (!"1".equals(json.getString("status"))) {
                throw new IOException("API error: " + json.getString("message"));
            }

            com.alibaba.fastjson.JSONArray result = json.getJSONArray("result");
            logger.info("wdy result={}", result);
            for (int i = 0; i < result.size(); i++) {
                com.alibaba.fastjson.JSONObject tx = result.getJSONObject(i);
                String receiverAddr = tx.getString("to");
                logger.info("wdy address="+address);
                logger.info("wdy tx="+tx);
                if(receiverAddr.equalsIgnoreCase(address)){
                    BizTxRecord record = new BizTxRecord();

                    record.setChainId(chainId);
                    record.setContractAddress("");
                    record.setBlockNumber(tx.getLong("blockNumber"));
                    record.setBlockTime(timestampToDate(tx.getLong("timeStamp")));
                    record.setHash(tx.getString("hash"));
                    record.setFromAddr(tx.getString("from"));
                    record.setToAddr(tx.getString("to"));
                    record.setValue(weiToEther(tx.getString("value")));
                    record.setConfirmations(tx.getLong("confirmations"));

                    records.add(record);
                }
            }
        }

        return records;
    }

    private static Date timestampToDate(Long timestamp) {
        return new Date(timestamp * 1000); // seconds to milliseconds
    }

    private static BigDecimal weiToEther(String wei) {
        return new BigDecimal(wei).divide(new BigDecimal("1000000000000000000")); // 1 ether = 1e18 wei
    }

    public static List<BizTxRecord> fetchTokenTxRecords(String chainId, String address, String contractAddress, int offset) throws IOException {
        List<BizTxRecord> records = new ArrayList<>();

        HttpUrl url = HttpUrl.parse(TaskConstants.BASE_URL).newBuilder()
                .addQueryParameter("chainid", chainId)
                .addQueryParameter("module", "account")
                .addQueryParameter("action", "tokentx")
                .addQueryParameter("contractaddress", contractAddress)
                .addQueryParameter("address", address)
                .addQueryParameter("page", "1")
                .addQueryParameter("offset", String.valueOf(offset))
                .addQueryParameter("sort", "desc")
                .addQueryParameter("apikey", TaskConstants.API_KEY)
                .build();

        Request request = new Request.Builder().url(url).get().build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected HTTP response code: " + response.code());
            }

            String body = response.body().string();
            com.alibaba.fastjson.JSONObject json = JSON.parseObject(body);

            if (!"1".equals(json.getString("status"))) {
                throw new IOException("API Error: " + json.getString("message"));
            }

            com.alibaba.fastjson.JSONArray result = json.getJSONArray("result");
            for (int i = 0; i < result.size(); i++) {
                com.alibaba.fastjson.JSONObject tx = result.getJSONObject(i);
                String receiverAddr = tx.getString("to");
                if(receiverAddr.equalsIgnoreCase(address)) {
                    BizTxRecord record = new BizTxRecord();

                    record.setChainId(chainId);
                    record.setContractAddress(contractAddress);
                    record.setBlockNumber(tx.getLong("blockNumber"));
                    record.setBlockTime(new Date(tx.getLong("timeStamp") * 1000));
                    record.setHash(tx.getString("hash"));
                    record.setFromAddr(tx.getString("from"));
                    record.setToAddr(tx.getString("to"));
                    record.setConfirmations(tx.getLong("confirmations"));

                    // 处理 tokenDecimal 和 value
                    String rawValue = tx.getString("value");
                    int decimals = tx.getIntValue("tokenDecimal");
                    BigDecimal divisor = BigDecimal.TEN.pow(decimals);
                    BigDecimal value = new BigDecimal(rawValue).divide(divisor);
                    record.setValue(value);

                    records.add(record);
                }
            }
        }

        return records;
    }



}
